﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Engine.Graphics;
using Engine.Physics;

namespace Engine
{
    struct GameObjectInitData
    {
        Dictionary<Type, object[]> componentData;
        string name;

        GameObjectInitData(string _name, Dictionary<Type, object[]> _componentData)
        {
            name = _name;
            componentData = _componentData;
        }
    }

    public class SceneManager : DrawableGameComponent
    {
        public static GameObject Origin { get; private set; }

        private static List<GameObject> toDestroy = new List<GameObject>();
        private Dictionary<string, GameObjectInitData> premades;

        private static bool inUpdateLoop;

        private bool drawSceneStructure = false;
        private static bool resetScheduled = false;
        private static bool loadSceneScheduled = false;

        public SceneManager(Game game)
            : base(game)
        {
            Origin = GameObject.Initialize("Origin");
            premades = new Dictionary<string, GameObjectInitData>();
        }

        public override void Update(GameTime _gameTime)
        {
            inUpdateLoop = true;
            Origin.Update(_gameTime);
            Origin.LateUpdate(_gameTime);
            inUpdateLoop = false;

            if (resetScheduled)
                Reset();

            if (loadSceneScheduled)
                LoadLevel();

            foreach (GameObject gameObject in toDestroy)
                gameObject.Destroy();

            if (InputHandler.KeyPressed(Microsoft.Xna.Framework.Input.Keys.F12))
            {
                drawSceneStructure = !drawSceneStructure;
            }

            if (drawSceneStructure)
            {
                List<string> pathList = new List<string>();
                Origin.PrintPathRecursivly(pathList);
                float spacing = 30;
                for (int i = 0; i < pathList.Count; i++)
                {
                    GraphicsEngine.Instance.DrawString(pathList[i], new Vector2(GraphicsEngine.Instance.Resolution.X - 10, spacing * i + 200), Color.Black, TextAlignement.Right);
                }
            }

            toDestroy.Clear();

            /*if (!gcTracker.IsAlive)
            {
                Console.WriteLine("A garbage collection occurred!");
                gcTracker = new WeakReference(new object());
            }*/
        }

        public override void Draw(GameTime _gameTime)
        {
            Origin.Draw();
        }

        public static void LoadLevel()
        {
            if (inUpdateLoop)
                loadSceneScheduled = true;
            else
            {
                loadSceneScheduled = false;
                Origin.Start();
            }
        }


        public static void Reset()
        {
            if (inUpdateLoop)
            {
                resetScheduled = true;
            }
            else
            {
                resetScheduled = false;
                Origin.Destroy();
                Origin = GameObject.Initialize("Origin");
            }
        }

        public static void InitializeNew(string _objectName)
        {

        }

        /// <summary>
        /// Find first node with the given path
        /// </summary>
        public static GameObject FindNode(string _path)
        {
            List<string> path = new List<string>(_path.Split('/'));
            GameObject currentGO = Origin;

            foreach (string name in path)
            {
                bool childFound = false;
                foreach (GameObject child in currentGO.Childs)
                {
                    if (child.Name == name)
                    {
                        currentGO = child;
                        childFound = true;
                        break;
                    }
                }

                foreach (GameObject child in currentGO.NewChilds)
                {
                    if (child.Name == name)
                    {
                        currentGO = child;
                        childFound = true;
                        break;
                    }
                }
                if (!childFound)
                {
                    Console.WriteLine("GameObject not found: " + _path);
                    return Origin;
                }
            }

            return currentGO;
        }

        /// <summary>
        /// Destroys given GameObject after the end of the current Update loop (instantly if not in loop)
        /// Doesn't delete external references to the object
        /// </summary>
        public static void Destroy(GameObject _object)
        {
            if (inUpdateLoop)
            {
                if(!toDestroy.Contains(_object))
                    toDestroy.Add(_object);
            }
            else
                _object.Destroy();
        }
    }
}